/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.sarco.bwi.lib;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author iktsup
 */
public final class Master {

    private Config config;
    private ServerSocket master;
    private Node[] nodes;
    private MessageThread messageThread;
    private int nodesDone = 0;
    private boolean jobIsDone = false;

    public Master(Config config) {
        if (config != null) {
            try {
                this.config = config;
                master = new ServerSocket(this.config.port);
                this.waitForConnections();
            } catch (UnknownHostException ex) {
                Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void waitForConnections() {
        int iNodes = this.config.nodes.length;
        this.nodes = new Node[iNodes];
        for (int i = 0; i < iNodes; i++) {
            try {
                System.out.println("Waiting for connections!");
                Socket client = this.master.accept();
                System.out.println("Got connection!");
                Node n = new Node();
                n.client = client;
                n.in = client.getInputStream();
                n.out = client.getOutputStream();
                this.nodes[i] = n;
            } catch (IOException ex) {
                Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        System.out.println("Starting job!");
        this.startJob();
    }

    public void startJob() {
        System.out.println("Sharing render jobs!");
        this.shareRenderJobs();
        //this.sendExecuteCommand();
        System.out.println("Sending execute command!");
        this.messageThread = new MessageThread();
        this.messageThread.run();
    }

    public void shareRenderJobs() {
        double framesEach = this.config.endFrame / this.nodes.length;
        System.out.println("Frames each: " + framesEach);
        for (int i = 0; i < this.nodes.length; i++) {
            try {
                byte[] bOut = new byte[128];
                int startFrame = (int) (this.config.startFrame +
                        (framesEach * i));
                int endFrame = (int) (startFrame + framesEach);
                if (i == this.nodes.length -1)
                    endFrame = this.config.endFrame;
                else
                    endFrame--;
                String s = Node.MASTER_SHARE_JOBS + startFrame
                        + Node.COMMON_SPLIT
                        + endFrame
                        + Node.COMMON_SPLIT;
                bOut = s.getBytes();
                this.nodes[i].out.write(bOut);
                this.nodes[i].out.flush();
            } catch (IOException ex) {
                Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void sendExecuteCommand() {
        for (int i = 0; i < this.nodes.length; i++) {
            try {
                this.nodes[i].out.write(Node.MASTER_EXECUTE.getBytes());
                this.nodes[i].out.flush();
            } catch (IOException ex) {
                Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public class MessageThread implements Runnable {
        public void run() {
            while (!jobIsDone)
                this.waitForMessages();
        }

        private void waitForMessages() {
            for (int i = 0; i < nodes.length; i++) {
                try {
                    String s = "";
                    byte[] bIn = new byte[128];
                    nodes[i].in.read(bIn);

                    for (int y = 0; y < bIn.length; y++) {
                        s += (char) bIn[y];
                    }

                    if (s.startsWith(Node.NODE_STATUS)) {
                        String msg = s.substring(Node.NODE_STATUS.length());
                        System.out.println(msg);
                    } else if (s.startsWith(Node.NODE_DONE)) {
                        nodesDone++;
                    }
                } catch (IOException ex) {
                    Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            if (nodesDone == nodes.length)
                jobIsDone = true;
        }
    }
}
